﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable StringLiteralTypo
// ReSharper disable UnusedParameter.Local

/* SimpleExpressions.cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System;
using System.Collections.Generic;

#endregion

#nullable enable

namespace AM.Memory.Collections.Linq;

public static partial class PoolingEnumerable
{
    /// <summary>
    ///
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static IPoolingEnumerable<T> Empty<T>() =>
        Range (0, 0).Select (x => (T)(object)x);

    /// <summary>
    ///
    /// </summary>
    /// <param name="startIndex"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public static IPoolingEnumerable<int> Range
        (
            int startIndex,
            int count
        )
    {
        return Pool<RangeExprEnumerable>.Get().Init (startIndex, count);
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="count"></param>
    /// <returns></returns>
    public static IPoolingEnumerable<int> Range (int count)
    {
        return Pool<RangeExprEnumerable>.Get().Init (0, count);
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="element"></param>
    /// <param name="count"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static IPoolingEnumerable<T> Repeat<T> (T element, int count) =>
        Range (0, count).Select (element, (item, x) => item);

    /// <summary>
    ///
    /// </summary>
    /// <param name="self"></param>
    /// <param name="element"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static bool Contains<T>
        (
            this IPoolingEnumerable<T> self,
            T element
        )
    {
        foreach (var item in self)
        {
            if (item!.Equals (element))
            {
                return true;
            }
        }

        return false;
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="self"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static int Count<T> (this IPoolingEnumerable<T> self)
    {
        var count = 0;
        foreach (var _ in self)
        {
            count++;
        }

        return count;
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="self"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static long LongCount<T> (this IPoolingEnumerable<T> self)
    {
        long count = 0;
        foreach (var _ in self)
        {
            count++;
        }

        return count;
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="self"></param>
    /// <param name="position"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    public static T ElementAt<T> (this IPoolingEnumerable<T> self, int position)
    {
        var i = 0;
        foreach (var item in self)
        {
            if (i == position)
            {
                return item;
            }

            i++;
        }

        throw new InvalidOperationException ("Sequence is too small. Index not found");
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="self"></param>
    /// <param name="other"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static bool SequenceEqual<T> (this IPoolingEnumerable<T> self, IPoolingEnumerable<T> other)
    {
        var comparer = EqualityComparer<T>.Default;
        using (var left = self.GetEnumerator())
        using (var right = other.GetEnumerator())
        {
            bool leftHas, rightHas;

            do
            {
                leftHas = left.MoveNext();
                rightHas = right.MoveNext();
                var equals = comparer.Equals (left.Current, right.Current);

                if (leftHas != rightHas || !equals)
                {
                    return false;
                }
            } while (leftHas && rightHas);

            return !leftHas && !rightHas;
        }
    }
}

internal class RangeExprEnumerable : IPoolingEnumerable<int>
{
    private int _start;
    private int _workCount;
    private int _count;

    public RangeExprEnumerable Init (int start, int count)
    {
        _start = start;
        _workCount = count;
        _count = 0;
        return this;
    }

    public IPoolingEnumerator<int> GetEnumerator()
    {
        _count++;
        return Pool<RangeExprEnumerator>.Get().Init (this, _start, _workCount);
    }

    private void Dispose()
    {
        if (_count == 0)
        {
            return;
        }

        _count--;
        if (_count == 0)
        {
            _start = _workCount = 0;
            _count = 0;
            Pool<RangeExprEnumerable>.Return (this);
        }
    }

    internal class RangeExprEnumerator : IPoolingEnumerator<int>
    {
        private int _start;
        private int _current;
        private int _workCount;
        private RangeExprEnumerable _parent = default!;

        public RangeExprEnumerator Init (RangeExprEnumerable parent, int start, int workCount)
        {
            _current = -1;
            _start = start;
            _workCount = workCount;
            _parent = parent;
            return this;
        }

        public bool MoveNext()
        {
            if (_current == 0)
            {
                return false;
            }

            if (_current == -1)
            {
                _current = _workCount;
                return _workCount != 0;
            }

            _current--;
            return _current != 0;
        }

        public void Reset() => _current = _start;

        object IPoolingEnumerator.Current => _current;

        public int Current => _start + (_workCount - _current);

        /// <inheritdoc cref="IDisposable.Dispose"/>
        public void Dispose()
        {
            _current = -1;
            _parent?.Dispose();
            _parent = default!;
            Pool<RangeExprEnumerator>.Return (this);
        }
    }

    IPoolingEnumerator IPoolingEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
